  /**
 * SafeCast logging application
 
 * by Robin Scheibler, 2011
 * safecast.org
 */

import processing.serial.*; // Serial library to read GPS and Geiger

// Serial port objects
Serial gpsPort;    // Create object from Serial class for the GPS port
Serial geigerPort; // Create object from Serial class for the Geiber port

// GPS device and measurement type
int GPSSerialId    = -1; // GPS Serial Id
int GeigerSerialId = -1; // Geiger Serial Id
int gpsDevId       = -1; // GPS device id
int measureId      = -1; // Measurement type id (air, water, ...)

// Logging File stuff
String dataFolder = "../data/";
String logfilename;       // log file name
PrintWriter logFile;
String taglogfilename;  // log file name for single point measurements
PrintWriter tagLogFile;

// Lists with the types of different stuff
String[] gpsDeviceTypes = { "Canmore GT-730FL-S" };
String[] measureTypes = { "Air", "Water", "Food" };

// State variables
boolean logging = false;
boolean connected = false;

// Geiger related variable
String CPM = "";        // Counts per minutes from Geiger counter

// GPS related variables
String gpsDataArray[];
String data = "";
Float Latitude1 = 0.0;
String Latitude = "";
String NorSindicator = "";
Float Longitude1 = 0.0;
String Longitude = "";
String EorWindicator = "";
String Satellites = "";
String Precision = "";
String  Altitude = "";
String gpsdata = "";
String time = "";
int Quality = 0;
String[] QualityDesc = {
    "0 - fix not available",
    "1 - GPS fix",
    "2 - Differential GPS fix",
    "3 = PPS fix",
    "4 = Real Time Kinematic",
    "5 = Float RTK",
    "6 = estimated (dead reckoning)",
    "7 = Manual input mode",
    "8 = Simulation mode"
};


// Start Setup Yeah
void setup() {
  size(sizeX,sizeY);
  smooth();
  frameRate(30);

  // set tentative file name
  logfilename = dataFolder + "safecast-" + nf(year(),4) + nf(month(),2) + nf(day(),2)
                + "-" + nf(hour(),2) + nf(minute(),2) + nf(second(),2) + ".log";
  taglogfilename = dataFolder + "safecast-tag-" + nf(year(),4)
                + nf(month(),2) + nf(day(),2)
                + "-" + nf(hour(),2) + nf(minute(),2) + nf(second(),2) + ".log";
  tagLogFile = createWriter(taglogfilename);

  // init the GUI
  init_GUI();
}

void draw() {
  background(128);

  if (connected)
  {
    if ( geigerPort.available() > 0) {  // If data is available,
      String cpm_from_srl
          = geigerPort.readStringUntil('\n');  // read it and store it in CPM
      if (cpm_from_srl != null)
      {
        CPM = cpm_from_srl.substring(0,cpm_from_srl.length()-2);
        readGPS(gpsPort);
        // write log
        writeLog();
      }
    }
    displayInfo();
  }
  delay(500);
}

void initSerial()
{
  gpsPort = new Serial(this, Serial.list()[GPSSerialId], 38400);
  geigerPort = new Serial(this, Serial.list()[GeigerSerialId], 9600);
}

void stopSerial()
{
  gpsPort.clear();
  gpsPort.stop();
  postStatusMessageln("GPS Serial port closed.");
  geigerPort.clear();
  geigerPort.stop();
  postStatusMessageln("Geiger Serial port closed.");
}

void openLogFile()
{
  logfilename = logfileTextField.getText();
  if (logfilename.equals(""))
  {
    // set tentative file name
    logfilename = dataFolder + "safecast-" + nf(year(),4) + nf(month(),2) + nf(day(),2)
      + "-" + nf(hour(),2) + nf(minute(),2) + nf(second(),2) + ".log";
  }
  logFile = createWriter(logfilename);
}

void closeLogFile()
{
  logFile.flush();
  logFile.close();
  postStatusMessageln("Log file close.");
  // set tentative file name
  logfilename = dataFolder + "safecast-" + nf(year(),4) + nf(month(),2) + nf(day(),2)
    + "-" + nf(hour(),2) + nf(minute(),2) + nf(second(),2) + ".log";
  logfileTextField.setText(logfilename);
}

void writeLog()
{
  if (logging)
  {
    String log_entry = formatLogEntry();
    logFile.println(log_entry);
    postStatusMessageln(log_entry);
    logFile.flush();
  }
}

String formatLogEntry()
{
  String log_entry =
    nf(year(),4) + "-" + nf(month(),2) + "-" + nf(day(),2) + "," +
    nf(hour(),2) + ":" + nf(minute(),2) + ":" + nf(second(),2) + "," +
    CPM + "," +
    Latitude + "," + NorSindicator + "," +
    Longitude + "," + EorWindicator + "," +
    Quality + "," + Satellites + "," +
    Precision + "," + Altitude + "," +
    gpsDeviceTypes[gpsDevId] + "," + measureTypes[measureId];

  return log_entry;
}

int checkParam()
{
  // assume everything is alright
  int status = 1;
  // check
  if (gpsDevId == -1)
  {
    postStatusMessageln("*** GPS Device Name not set.");
    status = 0;
  }
  if (measureId == -1)
  {
    postStatusMessageln("*** Measurement time not set.");
    status = 0;
  }
  if (GPSSerialId == -1)
  {
    postStatusMessageln("*** GPS Serial Port not set.");
    status = 0;
  }
  if (GeigerSerialId == -1)
  {
    postStatusMessageln("*** Geiger Serial Port not set.");
    status = 0;
  }
  return status;
}

void postStatusMessage(String msg)
{
  String text = statusDisplay.text();
  text = text + msg;
  String[] lines = split(text, '\n');
  if (lines.length > 50)
  {
    text = lines[lines.length-50] + "\n";
    for (int i=lines.length-49 ; i < lines.length ; i++)
      text = text + lines[i] + "\n";
  }
  statusDisplay.setText(text);
}

void postStatusMessageln(String msg)
{
  postStatusMessage(msg + "\n");
}

void startLogging()
{
  // open file
  openLogFile();
  // start running
  logging = true;
  postStatusMessageln("Started logging to " + logfilename);
}

void stopLogging()
{
  logging = false;
  closeLogFile();
  postStatusMessageln("Stopped Logging.");
}

void displayInfo()
{
  String line0 = "GEIGER+GPS DATA\n\n";
  String line1 = "";
  try {
    line1 = int(Integer.parseInt(CPM)*1000.0/350.0)/1000.0 + "uS (CPM: " + CPM + ")\n";
  } catch (NumberFormatException nfe) {
    line1 = "CPM: " + CPM + "\n";
  }
  String line2 = "Time: " 
      + nf(hour(),2) + ":" + nf(minute(),2) + ":" + nf(second(),2) + "\n";
  String line3 = "Latitude: " + Latitude1 + NorSindicator
      + ", Longitude: " + Longitude1 + EorWindicator + "\n";
  String line4 = "Altitude: " + Altitude + "\n";
  String line5 = 
      "Precision: " + Precision + ", Quality: " + QualityDesc[Quality]
      + ", Satellites: " + Satellites + "\n";
      
  nicePanel.setText(line0 + line1 + line2 + line3 + line4 + line5);
}

// function reads the gps data and sends it to the parsing function
void readGPS(Serial gpsPort) {
  int gotPosition = 0; 
  gpsPort.clear();
  while (gotPosition == 0)
  {
    while (gpsPort.available () <= 0);
    String data = gpsPort.readStringUntil('\n');
    if ( data != null) {
      gotPosition = parseString(data);
    }
  }
  gpsPort.clear();
}

//parsing function
int parseString (String serialString) {
  //sets up an array and splits the gps info by comma
  String items[] = split(serialString, ',');
  println("From GPS: " + serialString);
  if (items[0].equals("$GPGGA") && items.length >= 9) {  //looks for the GPGGA info
    getGGA(items);
    return 1;
  } else {
    return 0;
  }
}

//this function assigns the variables
//to array elements that have been parsed
void getGGA(String[] gpsinfo) {
  time = gpsinfo[1];
  float LatDeg = floor(float(gpsinfo[2])/100.0);
  float LatMin = float(gpsinfo[2]) - LatDeg*100.0;
  Latitude1 = LatDeg + LatMin/60.0;
  Latitude = gpsinfo[2];
  NorSindicator = gpsinfo[3];
  float LonDeg = floor(float(gpsinfo[4])/100.0);
  float LonMin = float(gpsinfo[4]) - LonDeg*100.0;
  Longitude1 = LonDeg + LonMin/60.0;
  Longitude = gpsinfo[4];
  EorWindicator = gpsinfo[5];
  Quality = int(gpsinfo[6]);
  Satellites = gpsinfo[7];
  Precision = gpsinfo[8];
  Altitude = gpsinfo[9];
  gpsdata = join(gpsinfo, ",");
}
